/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package monopoly;

import GameEvents.DiceEvents;
import GameEvents.Event;
import GameEvents.GameEvents;
import GameEvents.PaymentEvents;
import Monopol.Monopol;
import Monopol.MonopolExceptions.DeletePlayer;
import Monopol.MonopolExceptions.WrongCardType;
import Player.Player;
import java.util.Iterator;
import java.util.logging.Level;
import java.util.logging.Logger;
import monopoly.gamemanager.MonopolyGameManager;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Scanner;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import monopoly.results.EventArrayResult;
import monopoly.results.GameDetailsResult;
import monopoly.results.IDResult;
import monopoly.results.MonopolyResult;
import monopoly.results.PlayerDetailsResult;

/**
 *
 * @author blecherl
 */
public class MonopolyGame {

    public MonopolyGame() {
    }

    /**
     * Returns the Game board XSD
     * @return The String representive of the Game Board XSD
     */
    public String getGameBoardSchema() {
	// Create a new Input Stream
	InputStream is = null;
	try {
	    // Get the Resource Stream at 'Resources/Monopoly_Config.xml'
	    is = getClass().getClassLoader().getResourceAsStream("resources/monopoly_config.xsd");
	    // Return the String Using a Delimiter of End of Text \Z (RegEx)
	    return new Scanner(is).useDelimiter("\\Z").next();
	} finally {
	    // Verify that the Input Stream Object Exists
	    if (is != null) {
		// Close the Input stream
		try {
		    is.close();
		} catch (IOException ex) {
		}
	    }
	}
    }

    /**
     * Returns the Game board XML
     * @return The String representive of the Game Board XML
     */
    public String getGameBoardXML() {
	// Create a new Input Stream
	InputStream is = null;
	try {
	    // Get the Resource Stream at 'Resources/Monopoly_Config.xml'
	    is = getClass().getClassLoader().getResourceAsStream("resources/monopoly_config.xml");
	    // Return the String Using a Delimiter of End of Text \Z (RegEx)
	    return new Scanner(is).useDelimiter("\\Z").next();
	} finally {
	    // Verify that the Input Stream Object Exists
	    if (is != null) {
		// Close the Input stream
		try {
		    is.close();
		} catch (IOException ex) {
		}
	    }
	}
    }

    /**
     * Create a new Monopoly Game
     * @param gameName The Game Name
     * @param humanPlayers Total Number of Human Players
     * @param computerizedPlayers Total number of Computerized Players
     * @param useAutomaticDiceRoll Roll Dice Mode
     * @return A Monopoly Result Object
     */
    public MonopolyResult startGame(String gameName, int humanPlayers, int computerizedPlayers, boolean useAutomaticDiceRoll) {
	try {
	    // Check If the game is Started
	    if (MonopolyGameManager.getInstance().isGameStarted(gameName)) {
		return new MonopolyResult("A game '" + gameName + "' has already been started.");
	    } // Check if the Number of Humans players is OK
	    else if (humanPlayers < 0 || humanPlayers > 6) {
		return new MonopolyResult("Number of  Human players is out of range.'");
	    } // Check if the Number of Computer Player is OK
	    else if ((humanPlayers + computerizedPlayers) > 6 || (humanPlayers + computerizedPlayers) < 2) {
		return new MonopolyResult("Number of  Total  players is out of range.'");
	    } // Create a New Game
	    else {
		Boolean res;
		res = MonopolyGameManager.getInstance().addGame(gameName, humanPlayers, computerizedPlayers, useAutomaticDiceRoll);
		// Check the Reuslt
		if (!res) {
		    return new MonopolyResult("Currently we support only one game.");
		} else {
		    // The Game was successfuly created!
		    return new MonopolyResult();
		}
	    }
	} catch (Exception e) {
	    return new MonopolyResult("Error Creating Game!\n" + e.getMessage());
	}
    }

    /**
     * Returns the Game Details of an Input Game
     * @param gameName The Gmae Name we want the Details on
     * @return Teh Game Details as {@link GameDetailsResult}
     */
    public GameDetailsResult getGameDetails(String gameName) {//resume here remember you have game manager object
	// Check if the Game is NOT Started
	if (!MonopolyGameManager.getInstance().isGameStarted(gameName)) {
	    // There is no Such Game
	    return new GameDetailsResult(true, "NO SUCH GAME");
	} // The game Exists and Started
	else {
	    // Return the Game Details
	    String status;
	    // check if the Game is Active - Set the Status String
	    if (MonopolyGameManager.getInstance().isGameActive(gameName)) {
		// Game is Active
		status = "ACTIVE";
	    } else {
		// Game is Waiting
		status = "WAITING";
	    }
	    // Return the Game Details Result
	    return new GameDetailsResult(status, // Set the Game as Active
		    MonopolyGameManager.getInstance().getHumanPlayers(gameName), // Get the Number of Human Players
		    MonopolyGameManager.getInstance().getComputerizedPlayers(gameName), // Get the Number of Computer Players
		    MonopolyGameManager.getInstance().getJoinedHumanPlayers(gameName), // Get the Number of Joined Human Players
		    MonopolyGameManager.getInstance().isAutomaticDiceRoll(gameName)); // Get the Automatic Dice Roll Mode

	}

    }

    /**
     * Debug
     * @param args
     */
    public static void main(String[] args) {
	MonopolyGame myGame = new MonopolyGame();
	myGame.startGame("Assaf - Test", 2, 0, false);
	String[] waitingGames = myGame.getWaitingGames();
	String[] activeGames = myGame.getActiveGames();
	GameDetailsResult gameRes = myGame.getGameDetails(waitingGames[0]);
	System.out.println("Status: " + gameRes.getStatus() + "\n"
		+ "Joined Humans: " + gameRes.getJoinedHumanPlayers() + "\n"
		+ "Total Computers: " + gameRes.getTotalComputerPlayers() + "\n"
		+ "Total Humans: " + gameRes.getTotalHumanPlayers() + "\n"
		+ "Auto Dice:" + gameRes.isIsAutomaticDiceRoll());
	IDResult myID = myGame.joinGame(waitingGames[0], "Player 1");
	IDResult otherID = myGame.joinGame(waitingGames[0], "Player 2");
	System.out.println("Player ID:" + Integer.toString(myID.getResult()));
	System.out.println("Player ID:" + Integer.toString(otherID.getResult()));
	gameRes = myGame.getGameDetails(waitingGames[0]);
	System.out.println("Status: " + gameRes.getStatus() + "\n"
		+ "Joined Humans: " + gameRes.getJoinedHumanPlayers() + "\n"
		+ "Total Computers: " + gameRes.getTotalComputerPlayers() + "\n"
		+ "Total Humans: " + gameRes.getTotalHumanPlayers() + "\n"
		+ "Auto Dice:" + gameRes.isIsAutomaticDiceRoll());
	EventArrayResult a = null;
	while (true) {
	    a = myGame.getAllEvents(0);
	    Player player = Monopol.getInstance().getplayer(0, 280);
	    if (a.getLastEvent().getEventMessage().equals(" Prompt Player to Buy Asset")) {
		Integer id = a.getLastEvent().getEventID();
		myGame.buy(1, id, false);
//            } else if (a.getLastEvent().getEventMessage() == "Prompt Player to Roll Dice") {
//                Integer id = a.getLastEvent().getEventID();
//                myGame.setDiceRollResults(1, id, 1, 2);
	    }
// else
//     myGame.resign(1);
	}


    }

    /**
     * Get the Waiting Game
     * @return The Waiting Games as {@link String[]}
     */
    public String[] getWaitingGames() {
	String[] res = null;
	ArrayList<String> games = null; // Array that stores games
	try {
	    // Get all the Games
	    games = new ArrayList<String>(MonopolyGameManager.getInstance().getGamesNames());
	    // check each Game
	    Iterator<String> iterator = games.iterator();
	    while (iterator.hasNext()) {
		String game = iterator.next();
		// If the Game is Active
		if (MonopolyGameManager.getInstance().isGameActive(game)) {
		    // Remove from list
		    iterator.remove();
		}
	    }
	    // Set the String array Size as the New Games Size
	    res = new String[games.size()];
	    // Transform values of games to strings
	    for (int i = 0; i < games.size(); i++) {
		// Verfiy that the Game is not Null
		if (games.get(i) != null) {
		    // Save the Game in the Result Array
		    res[i] = games.get(i);
		}
	    }

	} catch (Exception e) {
	    e.printStackTrace();
	}
	// Return the Games Array
	return res; //return names
    }

    /**
     * Get the Active Games
     * @return The Active Games as {@link String[]}
     */
    public String[] getActiveGames() {
	String[] res = null;
	ArrayList<String> games = null; // Array that stores games
	try {
	    // Get all the Games
	    games = new ArrayList<String>(MonopolyGameManager.getInstance().getGamesNames());
	    // check each Game
	    Iterator<String> iterator = games.iterator();
	    while (iterator.hasNext()) {
		String game = iterator.next();
		// If the Game is Active
		if (!MonopolyGameManager.getInstance().isGameActive(game)) {
		    // Remove from list
		    iterator.remove();
		}
	    }
	    // Set the String array Size as the New Games Size
	    res = new String[games.size()];
	    // Transform values of games to strings
	    for (int i = 0; i < games.size(); i++) {
		// Verfiy that the Game is not Null
		if (games.get(i) != null) {
		    // Save the Game in the Result Array
		    res[i] = games.get(i);
		}
	    }

	} catch (Exception e) {
	    e.printStackTrace();
	}
	// Return the Games Array
	return res; //return names
    }

    /**
     * Joins a Player to the Game
     * @param gameName the Game Name to Join
     * @param playerName The Player Name to Join
     * @return The Player ID in the Game
     */
    public IDResult joinGame(String gameName, String playerName) {
	// Check that game name exists and not active
	if (!MonopolyGameManager.getInstance().isGameStarted(gameName))//if no game exist
	{
	    return new IDResult(true, "NO SUCH GAME", -1);//return an error that no game exist
	} else if (MonopolyGameManager.getInstance().isGameActive(gameName)) {
	    return new IDResult(true, "GAME ALREADY ACTIVE", -1);//game was already activated
	} else {
	    String[] players = MonopolyGameManager.getInstance().getPlayersNames(gameName);//get all games players
	    Boolean player_exist = false;

	    if (players != null) {
		for (String currPlayer : players) {
		    if (currPlayer.equals(playerName)) {
			player_exist = true;
			break;
		    }
		}
	    }

	    if (player_exist) {
		return new IDResult(true, "PLAYER ALREADY EXIST", -1);
	    }//game was already activated
	    else//player and game are valid
	    {
		System.out.println("player was added ");
		IDResult ID = null;
		try {
		    Integer playerID = MonopolyGameManager.getInstance().getJoinedHumanPlayers(gameName) + 1; // Set the Players Uniqe ID
		    MonopolyGameManager.getInstance().joinPlayer(
			    gameName, // Set the Game Name
			    playerName, // Set the Player Name
			    playerID // Set the Players Uniqe ID
			    );//added player
		    // Create a New ID Result
		    ID = new IDResult(playerID);
		} catch (Exception e) {
		    e.printStackTrace();
		}
		return ID;
	    }

	}
    }

    /**
     * Returns the Player Details in a Specific Game
     * @param gameName The Game Name
     * @return A {@link PlayerDetailsResult}Object With all the Players Details in the Game
     */
    public PlayerDetailsResult getPlayersDetails(String gameName) {
	// Check that the Game Exists
	if (!MonopolyGameManager.getInstance().isGameStarted(gameName)) {
	    // Non-Exsisting Game
	    return new PlayerDetailsResult("NO SUCH GAME");
	} else {
	    // REturn all the Game Player Details
	    return new PlayerDetailsResult(MonopolyGameManager.getInstance().getPlayersNames(gameName), // Set the Players Names Array
		    MonopolyGameManager.getInstance().getPlayersHumanity(gameName), // Set the Players Humanity Array
		    MonopolyGameManager.getInstance().getPlayersActive(gameName), // Set the Active Players Array
		    MonopolyGameManager.getInstance().getPlayersAmounts(gameName)); // Set the Players Amount Array
	}
    }

    /**
     * Return all the Monoply Game Events
     * @param eventID The Last Event ID the Player Have
     * @return An Array of the Monopoly Game Events
     */
    public EventArrayResult getAllEvents(int eventID) {
	final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock(true);
	final Lock read = readWriteLock.readLock();
	// Get current array result
	EventArrayResult Result = Monopol.getInstance().getEventArrayResult();
	try {
	    // Validate evendID against last eventID
	    if (eventID > Result.getSize() || eventID < 0) {
		// Non-Exsisting Event ID
		return new EventArrayResult("ASKING FOR NON EXISTING EVENT");
	    } // Check that the Event ID is the Same Size as the Current Game Events Size
	    else if (eventID == Result.getSize()) {
		// Return no Events and No Error - Try Again
		return new EventArrayResult();
	    } // New events happened since lthe last polling - Return the Newly Added Events
	    else {
		//read.lock();
		Event[] events = Result.getResults(); // Get the current game events Array
		Event[] newRes = new Event[events.length - eventID]; // Create a new Return array
		try {
		    // Copy the Events to the New Events Array
		    // Starting from the Last Event ID the Player has until the End
		    for (int j = 0, i = eventID; i < events.length; ++j, i++) {
			// Add the Cloned Event
			newRes[j] = events[i];
		    }

		} finally {
		    //read.unlock();
		}
		// Returning new events list for that user
		return new EventArrayResult(newRes);
	    }
	} catch (Exception e) {
	    e.printStackTrace();
	    return new EventArrayResult();
	}
    }


    /**
     * Invoke Setting the Dice Results
     * @param playerID The Player ID Currently Playing
     * @param eventID The Last Event ID of the Prompt To Set Dice Results
     * @param dice1 The First Dice Result
     * @param dice2 The Second Dice Result
     * @return {@link  MonopolyResult}
     */
    public MonopolyResult setDiceRollResults(int playerID, int eventID, int dice1, int dice2) {
	// Cancelling the timer
	    Monopol.getInstance().cancelTimer();
	//validate evendID against last eventID
	EventArrayResult Result = Monopol.getInstance().getEventArrayResult();
	// Get the Input Event from the Event ID
	Event resEvent = Result.getEvent(eventID);
	// Check if the Event ID is in the Scope of the Game Events
	if (resEvent == null) {
	    // Event Out of Scope
	    return new MonopolyResult("NO Matching Event ID");
	}
	// Check if the Player Name is Equal to the Referring Player Name
	if (!resEvent.getPlayerName().equals(MonopolyGameManager.getInstance().IDtoName(playerID))) {
	    // Not the same Player
	    return new MonopolyResult("NO Matching Player ID");
	}
	 //if the player lost
        if (Monopol.getInstance().getplayer(playerID, 0).getToDelete()){
           // Not the same Player
	    return new MonopolyResult("NO Matching Player ID");
        }
	// Validate the cubes values
	if (((dice1 > 6 || dice1 < 1) && (dice2 > 6 || dice2 < 1))) {
	    return new MonopolyResult("ILLEGAL CUBES VALUES");
	} else {
	    try {
		// Get the Event in the eventID Place - Should be a Game Event
		GameEvents prevEvent = (GameEvents) resEvent;
		// Add a New Event
		Result.addResults(new DiceEvents(
			prevEvent.getGameName(), // Set the Game Name
			Monopol.getInstance().getEventIDCounter(), // Set the Event ID of the New Event
			0, // Set the Timeout to 0 - No Timeout
			7, // Set the Event Type - Dice Roll
			prevEvent.getPlayerName(), // Set the Player Name
			"Dice Roll", // Set the Event Message
			prevEvent.getNextBoardSquareID(), // Set the Next Square Board ID
			dice1, // Set the First Dice Result
			dice2) // Set the Second Dice Result
			);
	    } catch (ClassCastException e) {
		// Not the Event We Wanted - Return an Error
		return new MonopolyResult("Last Event is not a Dice Event");
	    }
	   catch(Exception ex){
		ex.printStackTrace();
		return new MonopolyResult("Error on Setting Dices.\n" + ex.getMessage());
	    }
	    // Return an empty result
	    return new MonopolyResult();
	}
    }

    /**
     * Invoke a Resign Action
     * @param playerID The Player ID who wants to resign
     * @return {@link  MonopolyResult}
     */
    public MonopolyResult resign(int playerID) {
	//validate playerID against games players and players state
	String res = MonopolyGameManager.getInstance().IDtoName(playerID);
	// Check that the Player Name is Not Null - Player Exists
	if (res == null) {
	    // No Player Found
	    return new MonopolyResult("NOT MATCHING playerID");
        }
        else if (Monopol.getInstance().getplayer(playerID, 0).isHuman()==false) {
	    return new MonopolyResult("Computer player");
	}
        else if (Monopol.getInstance().getplayer(playerID, 0).getToDelete()==true) {
	    return new MonopolyResult("Player was already deleted ");
	}
            else {
	    // Delete Player from game
	    MonopolyGameManager.getInstance().resign(playerID);
	    // Return a Success Result
	    return new MonopolyResult();
	}
    }

    /**
     * Invoke a Buy Action
     * @param playerID The Player Id who wants to Buy
     * @param eventID The Last Event ID of the Prompt To Buy Action
     * @param buy Player Response - Indicates if the Player wants to buy or not
     * @return {@link MonopolyResult}
     */
    public MonopolyResult buy(int playerID, int eventID, boolean buy) {
	//cancel timer
	Monopol.getInstance().cancelTimer();
	// Validate evendID against last eventID
	EventArrayResult result = Monopol.getInstance().getEventArrayResult();
	// Get the Input Event from the Event ID
	Event resEvent = result.getEvent(eventID);
	// Checks if event ID and player ID are equal to the parameter in the original events
	if (resEvent == null) {
	    return new MonopolyResult("NOT MATCHING eventID");
	}
	// Get the current player and Check if it has enough Funds to Buy
	Player myPlayer = Monopol.getInstance().getplayer(
		playerID, // The Player ID To Find
		resEvent.getPaymentAmount() // The Amount of Money the player was requested to pay
	);
	//validate playerID against games players and players state
	if (myPlayer != null && !(resEvent.getPlayerName().equals(myPlayer.getName()))) {
	    return new MonopolyResult("NOT MATCHING playerID");
	}
	// Check if the Player wanted to Buy the Asset Or House
	if (buy) {
	    try {
		System.out.println("Bought Event is Sending");
		// Get the Event in the eventID Place - Should be a Payment Event
		PaymentEvents prevEvent = (PaymentEvents)resEvent;
		// Check if previous event was an asset auction
		if (prevEvent.getEventType() == 12  &&  myPlayer != null) {
		    //add  Asset buying event to events list
		    result.addResults(new PaymentEvents(
			    prevEvent.getGameName(), // Set the Game Name
			    Monopol.getInstance().getEventIDCounter(), // Set the Event ID of the New Event
			    14, // Set the Event Type - Asset Bought
			    prevEvent.getPlayerName(), // Set the Player Name
			    "Asset Bought", // Set the Event Message
			    prevEvent.getBoardSquareID(), // Set the Board Square ID
			    buy, // Pay to Treasury
			    buy, // Pay From the Player
			    null, // No Player Name
			    prevEvent.getPaymentAmount()) // Set the Amount to Pay
			    );
		} // Check if prev events was a house auction
		else if (prevEvent.getEventType() == 13 &&  myPlayer != null) {
		    //add  House buying event to events list
		    result.addResults(new PaymentEvents(
			    prevEvent.getGameName(), // Set the Game Name
			    Monopol.getInstance().getEventIDCounter(), // Set the Event ID of the New Event
			    15, // Set the Event Type - House Bought
			    prevEvent.getPlayerName(), // Set the Player Name
			    "House Bought", // Set the Event Message
			    prevEvent.getBoardSquareID(),
			    buy, // Pay to Treasury
			    buy, // Pay From the Player
			    null, // No Player Name
			    prevEvent.getPaymentAmount()) // Set the Amount to Pay
			    );
		} else {
		    return new MonopolyResult("Can't buy, not enough funds.");
		}
	    } catch (ClassCastException e) {
		// Not the Event We Wanted - Return an Error
		return new MonopolyResult("Last Event is not a Buy Event");
	    }
	    catch(Exception ex){
		ex.printStackTrace();
		return new MonopolyResult("Error on Buying\n" + ex.getMessage());
	    }
	}
	//return empty result
	return new MonopolyResult();
    }
}
